package com.flame.system.log.interceptor;

import java.nio.charset.StandardCharsets;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.flame.auth.core.context.AuthContextHolder;
import com.flame.auth.core.entity.TrustedPrincipal;
import com.flame.core.utils.DateUtils;
import com.flame.core.utils.HttpUtils;
import com.flame.system.log.config.SystemLogConfigurationProperties;
import com.flame.system.log.entity.OperateLog;
import com.flame.system.log.event.OperateCompeleteEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpHeaders;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

public class OperatorLogInterceptor implements HandlerInterceptor {

    private static final ThreadLocal<OperateLog> logThreadLocal = new ThreadLocal<>();

    private final SystemLogConfigurationProperties properties;

    private final ApplicationEventPublisher publisher;

    public OperatorLogInterceptor(ApplicationEventPublisher publisher, SystemLogConfigurationProperties properties) {
        this.publisher = publisher;
        this.properties = properties;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        String servletPath = request.getServletPath();

        // 判断是否加日志
        boolean includeFlag = false;
        AntPathMatcher matcher = new AntPathMatcher();
        for (String url : properties.getIncludeUrl()) {
            if (matcher.match(url, servletPath)) {
                includeFlag = true;
            }
        }
        if (!includeFlag) {
            return true;
        }

        // 判断是否不加日志
        for (String url : properties.getExcludeUrl()) {
            if (matcher.match(url, servletPath)) {
                return true;
            }
        }

        OperateLog operateLog = new OperateLog();
        operateLog.setOperateTime(DateUtils.currentDateTime());
        operateLog.setCompleteDuration(System.currentTimeMillis());
        logThreadLocal.set(operateLog);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
        ModelAndView modelAndView) throws Exception {
        TrustedPrincipal principal = AuthContextHolder.getContext();
        String account = "";
        if (principal != null) {
            account = principal.getAccount();
        }

        OperateLog operateLog = logThreadLocal.get();
        if (operateLog != null) {
            operateLog.setUserId(account);
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
        OperateLog operateLog = logThreadLocal.get();
        logThreadLocal.remove();
        if (operateLog == null) {
            return;
        }
        operateLog.setApiUrl(request.getServletPath());
        if (isDataRequest(request)) {
            ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) request;
            operateLog.setRequest(new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8));
        } else {
            operateLog.setRequest("文件数据");
        }
        if (isDataResponse(response)) {
            ContentCachingResponseWrapper responseWrapper = (ContentCachingResponseWrapper) response;
            operateLog.setResponse(new String(responseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8));
        } else {
            operateLog.setResponse("文件数据");
        }
        operateLog.setCompleteDuration(System.currentTimeMillis() - operateLog.getCompleteDuration());
        if (ex == null) {
            operateLog.setOperateStatus("0");
        } else {
            operateLog.setOperateStatus("1");
        }
        String ip = HttpUtils.getIp(request);
        String userAgent = request.getHeader(HttpHeaders.USER_AGENT);
        publisher.publishEvent(new OperateCompeleteEvent(operateLog, ip, userAgent));
    }

    public boolean isDataRequest(HttpServletRequest request) {
        String type = request.getContentType();
        if (!StringUtils.hasText(type)) {
            type = request.getHeader("Accept");
        }
        if (StringUtils.hasText(type)) {
            type = type.toLowerCase();
            return type.startsWith("application/json") || type.startsWith("application/xml");
        }
        return false;
    }

    public boolean isDataResponse(HttpServletResponse response) {
        String type = response.getContentType().toLowerCase();
        if (StringUtils.hasText(type)) {
            return type.startsWith("application/json") || type.startsWith("application/xml");
        }
        return false;
    }
}
